#pragma once

/**
 * This file contains:
 *
 * 1. CIE 1931 curves at sampled at 5nm intervals
 *
 * 2. CIE D65 spectrum sampled at 5nm intervals
 *    normalized to have unit luminance.
 *
 * 3. XYZ <-> sRGB conversion matrices
 *
 * 4. A convenience function "cie_interp" to access the discretized
 *    data at arbitrary wavelengths (with linear interpolation)
}
 */
#define CIE_LAMBDA_MIN 360.0f
#define CIE_LAMBDA_MAX 830.0f
#define CIE_SAMPLES    95

const float cie_x[CIE_SAMPLES] = {
    0.000129900000f, 0.000232100000f, 0.000414900000f, 0.000741600000f,
    0.001368000000f, 0.002236000000f, 0.004243000000f, 0.007650000000f,
    0.014310000000f, 0.023190000000f, 0.043510000000f, 0.077630000000f,
    0.134380000000f, 0.214770000000f, 0.283900000000f, 0.328500000000f,
    0.348280000000f, 0.348060000000f, 0.336200000000f, 0.318700000000f,
    0.290800000000f, 0.251100000000f, 0.195360000000f, 0.142100000000f,
    0.095640000000f, 0.057950010000f, 0.032010000000f, 0.014700000000f,
    0.004900000000f, 0.002400000000f, 0.009300000000f, 0.029100000000f,
    0.063270000000f, 0.109600000000f, 0.165500000000f, 0.225749900000f,
    0.290400000000f, 0.359700000000f, 0.433449900000f, 0.512050100000f,
    0.594500000000f, 0.678400000000f, 0.762100000000f, 0.842500000000f,
    0.916300000000f, 0.978600000000f, 1.026300000000f, 1.056700000000f,
    1.062200000000f, 1.045600000000f, 1.002600000000f, 0.938400000000f,
    0.854449900000f, 0.751400000000f, 0.642400000000f, 0.541900000000f,
    0.447900000000f, 0.360800000000f, 0.283500000000f, 0.218700000000f,
    0.164900000000f, 0.121200000000f, 0.087400000000f, 0.063600000000f,
    0.046770000000f, 0.032900000000f, 0.022700000000f, 0.015840000000f,
    0.011359160000f, 0.008110916000f, 0.005790346000f, 0.004109457000f,
    0.002899327000f, 0.002049190000f, 0.001439971000f, 0.000999949300f,
    0.000690078600f, 0.000476021300f, 0.000332301100f, 0.000234826100f,
    0.000166150500f, 0.000117413000f, 0.000083075270f, 0.000058706520f,
    0.000041509940f, 0.000029353260f, 0.000020673830f, 0.000014559770f,
    0.000010253980f, 0.000007221456f, 0.000005085868f, 0.000003581652f,
    0.000002522525f, 0.000001776509f, 0.000001251141f };

const float cie_y[CIE_SAMPLES] = {
    0.000003917000f, 0.000006965000f, 0.000012390000f, 0.000022020000f,
    0.000039000000f, 0.000064000000f, 0.000120000000f, 0.000217000000f,
    0.000396000000f, 0.000640000000f, 0.001210000000f, 0.002180000000f,
    0.004000000000f, 0.007300000000f, 0.011600000000f, 0.016840000000f,
    0.023000000000f, 0.029800000000f, 0.038000000000f, 0.048000000000f,
    0.060000000000f, 0.073900000000f, 0.090980000000f, 0.112600000000f,
    0.139020000000f, 0.169300000000f, 0.208020000000f, 0.258600000000f,
    0.323000000000f, 0.407300000000f, 0.503000000000f, 0.608200000000f,
    0.710000000000f, 0.793200000000f, 0.862000000000f, 0.914850100000f,
    0.954000000000f, 0.980300000000f, 0.994950100000f, 1.000000000000f,
    0.995000000000f, 0.978600000000f, 0.952000000000f, 0.915400000000f,
    0.870000000000f, 0.816300000000f, 0.757000000000f, 0.694900000000f,
    0.631000000000f, 0.566800000000f, 0.503000000000f, 0.441200000000f,
    0.381000000000f, 0.321000000000f, 0.265000000000f, 0.217000000000f,
    0.175000000000f, 0.138200000000f, 0.107000000000f, 0.081600000000f,
    0.061000000000f, 0.044580000000f, 0.032000000000f, 0.023200000000f,
    0.017000000000f, 0.011920000000f, 0.008210000000f, 0.005723000000f,
    0.004102000000f, 0.002929000000f, 0.002091000000f, 0.001484000000f,
    0.001047000000f, 0.000740000000f, 0.000520000000f, 0.000361100000f,
    0.000249200000f, 0.000171900000f, 0.000120000000f, 0.000084800000f,
    0.000060000000f, 0.000042400000f, 0.000030000000f, 0.000021200000f,
    0.000014990000f, 0.000010600000f, 0.000007465700f, 0.000005257800f,
    0.000003702900f, 0.000002607800f, 0.000001836600f, 0.000001293400f,
    0.000000910930f, 0.000000641530f, 0.000000451810f
};

const float cie_z[CIE_SAMPLES] = {
    0.000606100000f, 0.001086000000f, 0.001946000000f, 0.003486000000f,
    0.006450001000f, 0.010549990000f, 0.020050010000f, 0.036210000000f,
    0.067850010000f, 0.110200000000f, 0.207400000000f, 0.371300000000f,
    0.645600000000f, 1.039050100000f, 1.385600000000f, 1.622960000000f,
    1.747060000000f, 1.782600000000f, 1.772110000000f, 1.744100000000f,
    1.669200000000f, 1.528100000000f, 1.287640000000f, 1.041900000000f,
    0.812950100000f, 0.616200000000f, 0.465180000000f, 0.353300000000f,
    0.272000000000f, 0.212300000000f, 0.158200000000f, 0.111700000000f,
    0.078249990000f, 0.057250010000f, 0.042160000000f, 0.029840000000f,
    0.020300000000f, 0.013400000000f, 0.008749999000f, 0.005749999000f,
    0.003900000000f, 0.002749999000f, 0.002100000000f, 0.001800000000f,
    0.001650001000f, 0.001400000000f, 0.001100000000f, 0.001000000000f,
    0.000800000000f, 0.000600000000f, 0.000340000000f, 0.000240000000f,
    0.000190000000f, 0.000100000000f, 0.000049999990f, 0.000030000000f,
    0.000020000000f, 0.000010000000f, 0.000000000000f, 0.000000000000f,
    0.000000000000f, 0.000000000000f, 0.000000000000f, 0.000000000000f,
    0.000000000000f, 0.000000000000f, 0.000000000000f, 0.000000000000f,
    0.000000000000f, 0.000000000000f, 0.000000000000f, 0.000000000000f,
    0.000000000000f, 0.000000000000f, 0.000000000000f, 0.000000000000f,
    0.000000000000f, 0.000000000000f, 0.000000000000f, 0.000000000000f,
    0.000000000000f, 0.000000000000f, 0.000000000000f, 0.000000000000f,
    0.000000000000f, 0.000000000000f, 0.000000000000f, 0.000000000000f,
    0.000000000000f, 0.000000000000f, 0.000000000000f, 0.000000000000f,
    0.000000000000f, 0.000000000000f, 0.000000000000f
};

#define N(x) float(x / 10566.864005283874576)

const float cie_d65[CIE_SAMPLES] = {
    N(46.6383),  N(49.3637),  N(52.0891),  N(51.0323),  N(49.9755),  N(52.3118),  N(54.6482),  N(68.7015),
    N(82.7549),  N(87.1204),  N(91.486),   N(92.4589),  N(93.4318),  N(90.057),   N(86.6823),  N(95.7736),
    N(104.865),  N(110.936),  N(117.008),  N(117.41),   N(117.812),  N(116.336),  N(114.861),  N(115.392),
    N(115.923),  N(112.367),  N(108.811),  N(109.082),  N(109.354),  N(108.578),  N(107.802),  N(106.296),
    N(104.79),   N(106.239),  N(107.689),  N(106.047),  N(104.405),  N(104.225),  N(104.046),  N(102.023),
    N(100.0),    N(98.1671),  N(96.3342),  N(96.0611),  N(95.788),   N(92.2368),  N(88.6856),  N(89.3459),
    N(90.0062),  N(89.8026),  N(89.5991),  N(88.6489),  N(87.6987),  N(85.4936),  N(83.2886),  N(83.4939),
    N(83.6992),  N(81.863),   N(80.0268),  N(80.1207),  N(80.2146),  N(81.2462),  N(82.2778),  N(80.281),
    N(78.2842),  N(74.0027),  N(69.7213),  N(70.6652),  N(71.6091),  N(72.979),   N(74.349),   N(67.9765),
    N(61.604),   N(65.7448),  N(69.8856),  N(72.4863),  N(75.087),   N(69.3398),  N(63.5927),  N(55.0054),
    N(46.4182),  N(56.6118),  N(66.8054),  N(65.0941),  N(63.3828),  N(63.8434),  N(64.304),   N(61.8779),
    N(59.4519),  N(55.7054),  N(51.959),   N(54.6998),  N(57.4406),  N(58.8765),  N(60.3125)
};

#undef N

const float xyz_to_srgb[3][3] = {
    { 3.240479f, -1.537150f, -0.498535f },
    {-0.969256f,  1.875991f,  0.041556f },
    { 0.055648f, -0.204043f,  1.057311f }
};

const float srgb_to_xyz[3][3] = {
    { 0.412453f, 0.357580f, 0.180423f },
    { 0.212671f, 0.715160f, 0.072169f },
    { 0.019334f, 0.119193f, 0.950227f }
};

inline float cie_interp(const float *data, float x) {
    x -= CIE_LAMBDA_MIN;
    x *= (CIE_SAMPLES - 1) / (CIE_LAMBDA_MAX - CIE_LAMBDA_MIN);
    int offset = std::min(std::max(0, (int) x), CIE_SAMPLES - 2);
    float weight = x - offset;
    return (1.0f - weight) * data[offset] + weight * data[offset + 1];
}

inline float to_srgb(float value) {
    if (value <= 0.0031308f)
        return 12.92f * value;
    return 1.055f * std::pow(value, 1.f/2.4f) - 0.055f;
}
